package pool

import (
	"errors"
	"log"
	"sync"
)

type Sig struct{}

type Pool struct {
	Capacity int32 // pool容量，开启worker数量的上限，每一个worker绑定一个goroutine

	Running int32 // 正在执行任务的(worker/goroutine)数量

	FreeSignal uint64 //告诉阻塞的请求去绑定一个空闲的(worker/goroutine)

	Workers []*Worker //存放空闲worker，请求进入时会首先检查workers中是否有空闲worker，没有则阻塞

	Release chan Sig //用于关闭该Pool时告诉所有worker退出运行以防goroutine泄露,同时提示该pool处于关闭状态

	Lock sync.Mutex //支持Pool的同步操作 ???

	Once sync.Once //确保Pool关闭操作只会执行一次

	Cond *sync.Cond
}

func (p *Pool) Submit(task F) error {

	//判断pool是否已经关闭
	if len(p.Release) > 0 {
		log.Fatal("close")
		return errors.New("Pool Closed")
	}

	//获取一个可用worker
	w := p.GetWorker()

	//将任务交给当前可用woker进行执行
	w.Task <- task

	return nil
}

func (p *Pool) GetWorker() (w *Worker) {

	//从当前状态下从workers中取worker需加锁避免并发取woker
	p.Lock.Lock()
	workers := p.Workers
	n := len(workers) - 1

	//判断当前有无空闲worker
	if n > 0 { //有空闲worker

		for p.FreeSignal <= 0 {
			p.Cond.Wait()
		}
		w = workers[n]
		workers[n] = nil
		p.Workers = workers[:n]
		p.FreeSignal--
		p.Lock.Unlock()

	}

	//当前workers队列为空，即无空闲worker
	if n <= 0 { //无空闲worker

		if p.Running >= p.Capacity { //正在进行的worker大于等于pool容量

			//<-p.FreeSignal //释放阻塞信号 为空则会阻塞直到有值
			for p.FreeSignal <= 0 {
				p.Cond.Wait()
			}
			workers = p.Workers
			l := len(workers) - 1
			w = workers[l]
			workers[l] = nil
			p.Workers = workers[:l]
			p.FreeSignal--
			p.Lock.Unlock()

		} else { //当前无空闲worker但是Pool还没有满则直接新开一个worker

			p.Running++

			w = &Worker{
				Pool: p,
				Task: make(chan F),
			}

			w.Run()
			p.Lock.Unlock()
		}
	}

	return w

}

func (p *Pool) PutWorker(worker *Worker) {
	p.Lock.Lock()
	p.Workers = append(p.Workers, worker)
	p.FreeSignal++
	p.Lock.Unlock()
	p.Cond.Signal()
}
